/*
 * sixQuiPrend.c
 * projet Unix: 6 qui prend
 *
 *  @authors Doublier Sami
 *  @authors Pingot Alexandre
 */

#include "sixQuiPrend.h"

sharedMem *joueur_mem; /* structure pour lire en mémoire partagée */
sharedMem *shmAddr; /* retient l'adresse du segment de la mémoire partagée  */
sharedCounter * shmCounterAddr; /* retient l'adresse de la mémoire partagée du compteur de processus lecteurs */
int shmID; /* id de la mémoire partagée */
int shmIDCounter; /* id de la mémoire partagée du compteur de process lecteurs */
int semID; /* id du set de sémaphore utilisé */
int sock;
int nbrJoueurs;
int numeroJoueur;
fd_set * ensemble_lecture; /* contient stdin et le socket client */
struct itimerval timer;
struct itimerval old;
static struct sigaction sig;

void quitter() {
	SYS(close(sock));
	exit(2);
}

/* fonction appellée lors du lancement du jeu*/
void sixQuiPrend(int sck) {

	int istty;

	sig.sa_flags = SA_RESTART;
	sigemptyset(&sig.sa_mask);
	sig.sa_handler = signalHandler;

	SYS(sigaction(SIGALRM, &sig, NULL));

	/* Permer de remettre le timer à zéro */
	old.it_interval.tv_sec = 0;
	old.it_interval.tv_usec = 0;
	old.it_value.tv_sec = 0;
	old.it_value.tv_usec = 0;

	/* initialisation par defaut en attendant de reçevoir ceux du serveur */
	sock = sck;
	/* initialisation de la structure joueur_mem  qui contiendra les données lues en mémoire partagée */
	joueur_mem = (sharedMem*) malloc(sizeof(sharedMem));

	ensemble_lecture = (fd_set *) malloc(sizeof(fd_set));
	FD_SET(sock, ensemble_lecture);
	FD_SET(STDIN_FILENO, ensemble_lecture);

	/* le jeu commence */
	istty = isatty(0);
	if (istty) {
		printf("En attente d'autres joueurs ...\n");
	}
	receptionDesRessources();

	afficherPartie();

	debutDePartie();
}
void signalHandler(int signal) {
	if (signal == SIGALRM)
		fprintf(stderr, "Le temps est maintenant écoulé!\n");
}

/* Avant de démarrer la partie, le joueur doit recevoir plusieurs ressources du serveur :
 * -les informations sur la mémoire partagée */
void receptionDesRessources(void) {
	message msg; /* structure contenant le message */
	int readBytes; /* sert pour connaitre si on a reçu quelque chose du serveur */
	int messageNom = 0; /* sert pour connaitre si le serveur a demande au joueur son nom */
	int nomIntroduit = 0;
	char bufferNom[MAXLINE];

	/*************** ATTENTE DE LECTURE DU SEMID  ET DES CARTES ***************/
	/* La fonction recoit les ressources necessaires au jeu en TERMINANT par la
	 * reception des cartes ou alors ferme la connexion au serveur et ferme le programme */

	while (1) {
		int selectTest;
		fd_set rfds;
		FD_ZERO(&rfds);
		memcpy(&rfds, ensemble_lecture, sizeof(rfds));

		if ((selectTest = select(sock + 1, &rfds, 0, 0, NULL)) == -1) {
			if (errno == EINTR) {
				/* Le timer pour enregisrer le nom du joueur s'est ecoulé */
				SYS(setitimer(ITIMER_REAL, &old, NULL));
				if (nomIntroduit == 0) {
					/* Attribue au joueur un nom par defaut */
					message msg;
					sprintf(bufferNom, "Joueur");
					msg.type = NOM;
					strcpy(msg.content, bufferNom);
					SYS(write(sock,&msg,sizeof(message)));
					nomIntroduit = 1;
					printf("Un nom vous a été attribué");
				}
				messageNom = 0;
			}
			continue;
		}

		if (FD_ISSET(STDIN_FILENO, &rfds)) {
			/* Il y a quelque chose à lire sur stdin, c'est le nom du joueur */
			if (fgets(bufferNom, MAXLINE, stdin) != NULL) {
				if (messageNom) {
					message msgNom;
					if (nomIntroduit == 0) {
						msgNom.type = NOM;
						strcpy(msgNom.content, bufferNom);
						SYS(write(sock,&msgNom,sizeof(message)));
						nomIntroduit = 1;
						SYS(setitimer(ITIMER_REAL, &old, NULL));
						printf("Votre nom est enregistré !\n");
					}
				}
			}
		}

		else if (FD_ISSET(sock, &rfds)) {
			/* Message du serveur ! */
			bzero(&msg, sizeof(message));
			/* on boucle en attendant de recevoir les id des 2 mémoires partagées et du sémaphoreSetID */
			SYS(readBytes = read(sock,&msg,sizeof(message)));

			if (readBytes != 0) { /* on a lu quelque chose du serveur */
				switch (msg.type) {

					case CLOSE:
						/* le client doit se terminer , on ne peut pas l'accepter pour jouer */
						printf("%s \n", msg.content);
						printf("Deconnexion..... \n");
						quitter();

					case BIENVENUE:
						printf("%s \n", msg.content);
						break;

					case SEMAPHORE_SET_ID:
						semID = atoi(msg.content);
						break;

					case SHARED_MEM:
						shmID = atoi(msg.content);
						break;

					case SHARED_MEM_RC:
						shmIDCounter = atoi(msg.content);
						break;

					case MESSAGE:
						printf("%s \n", msg.content);
						break;

					case NUMERO:
						numeroJoueur = atoi(msg.content);
						break;

					case NOM:
						printf("%s \n", msg.content);
						messageNom = 1;
						/* On demarre le timer pour que le client introduise son nom*/
						printf("Attention vous n'avez que %d secondes !\n",
								TIMEOUT_CLI);
						timer.it_value.tv_sec = TIMEOUT_CLI;
						SYS(setitimer(ITIMER_REAL, &timer, NULL));
						break;

					 case CARTES_RECUES:
					 /*Initialisation de la memoire partagée pour récuperer les informations necessaires */

					 shmAddr = (sharedMem *) shmAttach(shmID, (char*) shmAddr);
					 shmCounterAddr = (sharedCounter *) shmAttach(shmIDCounter,(char*) shmCounterAddr);

					 lecteur(joueur_mem, shmAddr, shmCounterAddr, semID);
					 nbrJoueurs = joueur_mem->joueurs;

					 /*traiterCartesRecues(msg.content); */
					 return;

					default:
						break;
					}
			}
		}
	}
}


/* Affichage des cartes sur la table et des cartes en possession du joueur, cette méthode est appelée à chaque fois que le joueur doit
 * proposer une carte */
void afficherPartie() { /* changer le nom en afficherTour car, on fait appel à cette méthode chaque fois*/
	int i, j;

	/* maj de la memoire partagee */
	lecteur(joueur_mem, shmAddr, shmCounterAddr, semID);

	printf("\n---------- Cartes sur la table ----------\n");

	/* les cartes visibles */
	printf("\nCartes faces visibles : \n");
	for (i = 0; i < CARTES_LIGNE; i++) {
		for (j = 0; j < CARTES_COLONNE; j++) {
				printf("\t%3d", joueur_mem->cartesSurLaTable[i][j]);
		}
		printf("\n");
	}
	printf("\n-----------------------------------------\n");

	/* Affichage des cartes que le joueur possède */
	for (i = 0; i < nbrJoueurs; i++) {
		if (i != numeroJoueur) {
			continue;
		}
		printf("Vos cartes restantes : ");
		for (i = 0; i < MAX_CARTES_PAR_JOUEUR; i++) {
			int carte = joueur_mem->visible[numeroJoueur][i];
			if (carte != VIDE) {
				printf("%3d ", carte);
			}
		}
		printf("\n");
	}
	printf("%s", SEPARATEUR);
}

void debutDePartie() { /*Ici ou autre part, il faut que le serveur retourne la première carte de chaque ligne*/
	int readBytes;
	int faireUneProposition = 0; /* Indique si c'est au joueur de proposer une carte */

	while (1) {
		message msg;
		int selectTest;
		fd_set rfds;
		FD_ZERO(&rfds);
		memcpy(&rfds, ensemble_lecture, sizeof(rfds));

		if ((selectTest = select(sock + 1, &rfds, NULL, NULL, NULL)) == -1) {
			if (errno == EINTR) {
				message msg;
				char carte[10];
				/* Le timer pour proposer une carte est écoulé !
				 * Envoie d'une carte avec la valeur VIDE soit 0 que le serveur ne va pas traiter */
				 SYS(setitimer(ITIMER_REAL, &old, NULL));
				 sprintf(carte, "%d", VIDE);
				 msg.type = CARTE;
				 strcpy(msg.content, carte);
				 SYS(write(sock,&msg,sizeof(message)));
			}
			faireUneProposition = 0;
			continue;
		}

		if (FD_ISSET(STDIN_FILENO, &rfds)) {
			/* Il y a quelque chose à lire sur stdin : la valeur de la carte proposée */
			char str[MAXLINE];
			int carte;
			if (fgets(str, MAXLINE, stdin) != NULL) {
				if (faireUneProposition) {
					message msg;
						carte = atoi(str);
						msg.type = CARTE;
						sprintf(msg.content, "%d", carte);
						SYS(write(sock,&msg,sizeof(message)));
						/* La carte est envoyée, on arrête le timer */
						SYS(setitimer(ITIMER_REAL, &old, NULL));
						faireUneProposition = 0;
				}
			}
		}

		else if (FD_ISSET(sock, &rfds)) {
			bzero(&msg, sizeof(message));

			SYS(readBytes=read(sock,&msg,sizeof(message)));

			if (readBytes != 0) { /* Message du serveur ! */
				char str[MAXLINE];
				int rangee;
				switch (msg.type) {

				case CLOSE:
					printf("%s \n", msg.content);
					printf("Deconnexion... \n");
					quitter();
					break;

				case CARTE:
					afficherPartie();
					/* C'est au joueur de proposer une carte */
					printf("%s \n", msg.content);
					printf("\nChoisissez une carte : \n");
					faireUneProposition = 1;
					printf("Attention vous n'avez que %d secondes !\n",TIMEOUT_CLI);
					timer.it_value.tv_sec = TIMEOUT_CLI;
					SYS(setitimer(ITIMER_REAL, &timer, NULL));
					break;

				case CHOIX_RANGEE:
					/*Le joueur choisi la rangée qu'il veut ramasser*/
					printf("%s \n", msg.content);
					printf("Attention vous n'avez que %d secondes !\n",TIMEOUT_CLI);
					timer.it_value.tv_sec = TIMEOUT_CLI;
					SYS(setitimer(ITIMER_REAL, &timer, NULL));

					if (fgets(str, MAXLINE, stdin) != NULL) {
						rangee = atoi(str);
						msg.type = CHOIX_RANGEE;
						sprintf(msg.content, "%d", rangee);
						SYS(write(sock,&msg,sizeof(message)));
						/* La carte est envoyée, on arrête le timer */
						SYS(setitimer(ITIMER_REAL, &old, NULL));
					}

					break;

				case GAGNANT_PARTIE:
					printf("%s \n", msg.content);
					break;

				case MESSAGE:
					printf("%s \n", msg.content);
					break;


				case GAGNANT_PAR_DEFAUT:
					printf("%s \n", msg.content);
					quitter();
					break;

				case AFFICHER_POINTS:
					afficherPoints();
					break;

				default:
					fprintf(stderr, "Message %d du serveur inconnu : %s",
							msg.type, msg.content);
					break;
				}
			}
		}

	}
}

/* Vérifie si la proposition du joueur correspond bien à une carte de son propre jeu */
int verifierCarte(char *carte) { /* a changer en verifier carte*/
	char *ptr;
	int noCarte;/*, i;*/
	noCarte = strtol(carte, &ptr, 10);
	if (*ptr != '\0')
		return -1;
	return -1;/*renvoyé -2 si ce n'est pas une de ces cartes*/
}

void afficherPoints(){
	int i;
		/* maj de la memoire partagee */
		lecteur(joueur_mem, shmAddr, shmCounterAddr, semID);


		printf("\n---------- Les points ----------\n");
		for (i = 0; i < MAX_JOUEURS; i++) {
			int pointDuJoueur = joueur_mem->totaux_points[i];
			printf("%s :\t%3d points \n", joueur_mem->nom[i], pointDuJoueur);
		}
		printf("\n-----------------------------------------\n");

		printf("%s", SEPARATEUR);
}
